﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// In the graylevel mode the dilation operation bright regions surrounded by dark
    /// regions grow in size, and dark regions surrounded by bright regions shrink in
    /// size. Small dark spots in images will disappear as they are `filled in' to the
    /// surrounding intensity value. Small bright spots will become larger spots. The
    /// effect is most marked at places in the image where the intensity changes rapidly
    /// and regions of fairly uniform intensity will be largely unchanged except at their
    /// edges.
    /// </summary>
    public class GrayDilation : BaseImageFilter
    {
        protected byte[,]  m_aSelection = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="GrayDilation"/> class.
        /// </summary>
        public GrayDilation()
            : this(new byte[,]{
            {1,1,1},
            {1,1,1},
            {1,1,1}}
        )
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GrayDilation"/> class.
        /// </summary>
        /// <param name="_sele">The _sele.</param>
        public GrayDilation(byte[,] _sele)
        {
            m_aSelection = _sele;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GrayDilation"/> class.
        /// </summary>
        /// <param name="_grayDilation">The _gray dilation.</param>
        internal GrayDilation(GrayDilation _grayDilation)
        {
            m_aSelection = _grayDilation.m_aSelection;
        }

        /// <summary>
        /// Gets or sets the selection.
        /// </summary>
        /// <value>The selection.</value>
        public byte[,] Selection
        {
            get { return m_aSelection; }
            set { m_aSelection = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap tempbmp = (UnsafeBitmap)_bitmap.Clone();

            BitmapData data2 = tempbmp.BitmapData; //tempbmp.LockBits(new Rectangle(0, 0, tempbmp.Width, tempbmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData data = _bitmap.BitmapData; //bmpimg.LockBits(new Rectangle(0, 0, bmpimg.Width, bmpimg.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            byte[,] sElement = m_aSelection;

            unsafe
            {
                byte* ptr = (byte*)data.Scan0;
                byte* tptr = (byte*)data2.Scan0;

                ptr += data.Stride + 3; //ptr += data.Stride + 3;
                tptr += data.Stride + 3; //tptr += data.Stride + 3;

                int remain = data.Stride - data.Width * 3; //data.Stride - data.Width * 3;

                for (int i = 1; i < data.Height - 1; i++)
                {
                    for (int j = 1; j < data.Width - 1; j++)
                    {
                        byte* temp = ptr - data.Stride - 3;
                        byte max = 0;

                        for (int k = 0; k < 3; k++)
                        {
                            for (int l = 0; l < 3; l++)
                            {
                                if (max < temp[data.Stride * k + l * 3]) //if (max < temp[data.Stride * k + l * 3])
                                {
                                    if (sElement[k, l] != 0)
                                    {
                                        max = temp[data.Stride * k + l * 3]; //max = temp[data.Stride * k + l * 3];
                                    }
                                }
                            }
                        }

                        tptr[0] = tptr[1] = tptr[2] = max;

                        ptr += 3;   //ptr += 3;
                        tptr += 3;  //tptr += 3;
                    }
                    ptr += remain + 6; //ptr += remain + 6;
                    tptr += remain + 6; //tptr += remain + 6;
                }
            }

            _bitmap.Dispose(); //bmpimg.UnlockBits(data);
            tempbmp.Dispose(); //tempbmp.UnlockBits(data2);

            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new GrayDilation(this);
        }
    }
}
